import OSS from 'ali-oss';
import { v4 as uuidv4 } from 'uuid';

/**
 * 阿里云OSS配置接口
 */
export interface OSSConfig {
  region: string;       // OSS区域，例如 'oss-cn-hangzhou'
  accessKeyId: string;  // 访问密钥ID
  accessKeySecret: string; // 访问密钥密码
  bucket: string;       // OSS存储桶名称
  secure?: boolean;     // 是否使用HTTPS，默认true
  cname?: boolean;      // 是否使用自定义域名，默认false
  endpoint?: string;    // 自定义域名
}

/**
 * 上传结果接口
 */
export interface UploadResult {
  url: string;          // 文件访问URL
  name: string;         // 文件名
  path: string;         // 文件路径
  size: number;         // 文件大小(字节)
  type: string;         // 文件类型
  success: boolean;     // 上传是否成功
  error?: Error;        // 如果上传失败，包含错误信息
}

/**
 * 阿里云OSS工具类
 */
export class AliOssUtil {
  private client: OSS;
  private baseUrl: string;
  
  /**
   * 构造函数
   * @param config OSS配置
   */
  constructor(config: OSSConfig) {
    this.client = new OSS({
      region: config.region,
      accessKeyId: config.accessKeyId,
      accessKeySecret: config.accessKeySecret,
      bucket: config.bucket,
      secure: config.secure !== false, // 默认使用HTTPS
      cname: config.cname || false,
      endpoint: config.endpoint,
    });
    
    // 构建基础URL
    if (config.endpoint) {
      this.baseUrl = `${config.secure !== false ? 'https' : 'http'}://${config.region}/`;
    } else {
      this.baseUrl = `${config.secure !== false ? 'https' : 'http'}://${config.bucket}.${config.region}.aliyuncs.com/`;
    }
  }
  
  /**
   * 生成唯一文件名
   * @param originalName 原始文件名
   * @returns 唯一文件名
   */
  private generateUniqueFileName(originalName: string): string {
    const ext = originalName.includes('.') 
      ? originalName.substring(originalName.lastIndexOf('.')) 
      : '';
    return `${uuidv4()}${ext}`;
  }
  
  /**
   * 上传文件
   * @param file 文件对象
   * @param directory 存储目录，例如 'images/'
   * @param useOriginalName 是否使用原始文件名，默认false（使用UUID生成唯一文件名）
   * @returns 上传结果
   */
  async uploadFile(file: File, directory: string = '', useOriginalName: boolean = false): Promise<UploadResult> {
    try {
      // 确保目录以斜杠结尾
      const dir = directory ? (directory.endsWith('/') ? directory : `${directory}/`) : '';
      
      // 生成文件名
      const fileName = useOriginalName ? file.name : this.generateUniqueFileName(file.name);
      const ossPath = `${dir}${fileName}`;
      
      // 获取文件的二进制数据
      const bytes = await file.arrayBuffer();
      const buffer = Buffer.from(bytes);

      console.log('file.type', file.type)

      let headers = {
        'Content-Type': file.type,
        'Content-Disposition': `inline`
      }
      
      // 执行上传
      const result = await this.client.put(ossPath, buffer, {
        headers: headers
      });
      
      return {
        url: `${this.baseUrl}${ossPath}`,
        name: fileName,
        path: ossPath,
        size: file.size,
        type: file.type,
        success: true
      };
    } catch (error) {
      console.error('OSS上传文件失败:', error);
      
      return {
        url: '',
        name: file.name,
        path: '',
        size: file.size,
        type: file.type,
        success: false,
        error: error instanceof Error ? error : new Error(String(error))
      };
    }
  }
  
  /**
   * 上传Base64图片
   * @param base64Data Base64编码的图片数据
   * @param directory 存储目录
   * @param fileName 文件名（可选，默认生成UUID）
   * @returns 上传结果
   */
  async uploadBase64(base64Data: string, directory: string = '', fileName?: string): Promise<UploadResult> {
    try {
      // 解析Base64数据
      const matches = base64Data.match(/^data:([A-Za-z-+/]+);base64,(.+)$/);
      if (!matches || matches.length !== 3) {
        throw new Error('无效的Base64数据格式');
      }
      
      const type = matches[1];
      const buffer = Buffer.from(matches[2], 'base64');
      
      // 确定文件扩展名
      let ext = '.png'; // 默认扩展名
      if (type.includes('jpeg') || type.includes('jpg')) ext = '.jpg';
      else if (type.includes('png')) ext = '.png';
      else if (type.includes('gif')) ext = '.gif';
      else if (type.includes('webp')) ext = '.webp';
      
      // 确保目录以斜杠结尾
      const dir = directory ? (directory.endsWith('/') ? directory : `${directory}/`) : '';
      
      // 生成文件名
      const name = fileName || `${uuidv4()}${ext}`;
      const ossPath = `${dir}${name}`;
      
      // 执行上传
      const result = await this.client.put(ossPath, buffer);
      
      return {
        url: `${this.baseUrl}${ossPath}`,
        name,
        path: ossPath,
        size: buffer.length,
        type,
        success: true
      };
    } catch (error) {
      console.error('OSS上传Base64图片失败:', error);
      
      return {
        url: '',
        name: fileName || 'unknown',
        path: '',
        size: 0,
        type: '',
        success: false,
        error: error instanceof Error ? error : new Error(String(error))
      };
    }
  }
  
  /**
   * 删除文件
   * @param ossPath 文件在OSS中的路径
   * @returns 是否删除成功
   */
  async deleteFile(ossPath: string): Promise<boolean> {
    try {
      await this.client.delete(ossPath);
      return true;
    } catch (error) {
      console.error('OSS删除文件失败:', error);
      return false;
    }
  }
  
  /**
   * 批量删除文件
   * @param ossPaths 文件路径数组
   * @returns 删除结果数组
   */
  async deleteMultiple(ossPaths: string[]): Promise<boolean[]> {
    try {
      const result = await this.client.deleteMulti(ossPaths, { quiet: true });
      // 如果使用quiet模式，则只返回失败的结果，所以我们假设所有未返回的都是成功的
      const deletedObjects = result.deleted || [];
      
      // 创建结果数组，默认所有操作都成功
      const deleteResults = ossPaths.map(() => true);
      
      // 如果有失败的，更新对应的结果
      deletedObjects.forEach((item: any, index: number) => {
        if (item && item.code) { // 如果有错误码，说明删除失败
          deleteResults[index] = false;
        }
      });
      
      return deleteResults;
    } catch (error) {
      console.error('OSS批量删除文件失败:', error);
      return ossPaths.map(() => false);
    }
  }
  
  /**
   * 获取文件访问URL
   * @param ossPath 文件在OSS中的路径
   * @param expireTime URL有效期(秒)，默认3600秒(1小时)
   * @returns 临时访问URL
   */
  async getSignedUrl(ossPath: string, expireTime: number = 3600): Promise<string> {
    try {
      const result = await this.client.signatureUrl(ossPath, {
        expires: expireTime
      });
      return result;
    } catch (error) {
      console.error('OSS获取签名URL失败:', error);
      return '';
    }
  }
  
  /**
   * 检查文件是否存在
   * @param ossPath 文件在OSS中的路径
   * @returns 文件是否存在
   */
  async isFileExist(ossPath: string): Promise<boolean> {
    try {
      await this.client.head(ossPath);
      return true;
    } catch (error) {
      // 如果文件不存在，OSS会返回404错误
      if ((error as any).code === 'NoSuchKey') {
        return false;
      }
      console.error('OSS检查文件存在性失败:', error);
      return false;
    }
  }
  
  /**
   * 列出目录下的文件
   * @param directory 目录路径，例如 'images/'
   * @param maxKeys 最大返回条目数，默认100
   * @returns 文件列表
   */
  async listFiles(directory: string = '', maxKeys: number = 100): Promise<OSS.ObjectMeta[]> {
    try {
      const result = await this.client.list({
        prefix: directory,
        'max-keys': maxKeys
      }, {});
      return result.objects || [];
    } catch (error) {
      console.error('OSS列出文件失败:', error);
      return [];
    }
  }
}

/**
 * 创建OSS客户端实例
 * @param config OSS配置
 * @returns OSS工具类实例
 */
export function createOssClient(config: OSSConfig): AliOssUtil {
  return new AliOssUtil(config);
}

// 导出默认实例(需要在环境变量中配置OSS信息)
let defaultOssClient: AliOssUtil | null = null;

/**
 * 获取默认OSS客户端实例
 * @returns OSS工具类实例
 */
export function getDefaultOssClient(): AliOssUtil {
  if (!defaultOssClient) {
    // 从环境变量中获取配置
    const config: OSSConfig = {
      region: process.env.OSS_REGION || '',
      accessKeyId: process.env.OSS_ACCESS_KEY_ID || '',
      accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET || '',
      bucket: process.env.OSS_BUCKET || '',
      secure: process.env.OSS_SECURE !== 'false',
      endpoint: process.env.OSS_ENDPOINT
    };
    
    // 验证必要的配置
    if (!config.region || !config.accessKeyId || !config.accessKeySecret || !config.bucket) {
      throw new Error('OSS配置不完整，请检查环境变量');
    }
    
    defaultOssClient = new AliOssUtil(config);
  }
  
  return defaultOssClient;
}

export default {
  createOssClient,
  getDefaultOssClient
};
